#include "global.h"

// Declare the body function as extern so we can link it
extern void processBody();

// Attempts to fork a new process into the ready queue and initalizes
// its stack area. Assigns the new process as a child of running
int kfork()
{
	Process* newProcess = dequeue(&freeQueue);
	if (newProcess)
	{
		int i, segment = 0x1000;
		char userProg[10];

		// Initialize CPU registers
		for (i = 1; i < 10; ++i)
		{
			newProcess->stack[STACK_SIZE - i] = 0;
		}
		
		// Initialize the new process
		newProcess->stack[STACK_SIZE - 1] = (int)processBody;
		newProcess->stackPointer = &(newProcess->stack[STACK_SIZE - 9]);		
		newProcess->parentProcessID = running->processID;
		newProcess->status = READY;
		newProcess->numChildren = 0;
		enqueue(newProcess, &readyQueue);
		
		// Up the parent child count
		++running->numChildren;
		
		// Copy all fds from the parent
		for (i = 0; i < NUM_FILE_DESC; ++i)
		{
			newProcess->fileDescriptors[i] = running->fileDescriptors[i];
			if (running->fileDescriptors[i] != 0)
			{
				++running->fileDescriptors[i]->refCount;
				if (running->fileDescriptors[i]->mode == READ_PIPE)
					++running->fileDescriptors[i]->pipePtr->numReader;
				else if (running->fileDescriptors[i]->mode == WRITE_PIPE)
					++running->fileDescriptors[i]->pipePtr->numWriter;
			}
		}
		
		// Load the user mode program into the processes memory space
		segment *= newProcess->processID + 1;
		strcpy(userProg, "/bin/u1");
		load(userProg, segment);
		
		// Initialize the user stack
		putWord(0x0200, segment, 0x1000 - 2);	// Set the flag register
		putWord(segment, segment, 0x1000 - 4);	// Set the flag register
		// Set ax, bx, cx, dx, bp, si, di to 0
		for (i = 6; i < 21; i += 2)
		{
			putWord(0, segment, 0x1000 - i);
		}
		putWord(segment, segment, 0x1000 - 22);	// Set the uES to seg
		putWord(segment, segment, 0x1000 - 24);	// Set the uDS to seg
		
		// Set the uss and usp of the new process
		newProcess->userStackSegment = segment;
		newProcess->userStackPointer = 0x1000 - 24;
		
		return newProcess->processID;
	}
	return -1;
}
